using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Xml;
using System.Xml.Linq;
using RESTAgent.BasicMediaTypes.Hal;

namespace RESTAgent.BasicMediaTypes {
    public static class HttpContentHalExtensions {


        public static void WriteAsXml(this HalDocument halDocument, Stream stream) {
            using (var writer = XmlWriter.Create(stream)) {
                writer.WriteStartDocument();
                halDocument.Root.WriteAsXml(writer);
                writer.WriteEndDocument();
            }
        }

        public static void WriteAsXml(this HalResource halResource, XmlWriter writer) {
            writer.WriteStartElement("resource");
            writer.WriteAttributeString("rel", halResource.Rel);
            writer.WriteAttributeString("href",halResource.Href);
            if (!string.IsNullOrEmpty(halResource.Name)) writer.WriteAttributeString("name", halResource.Name);

            foreach (var halNode in halResource.Contents.Values) {
                if (halNode is HalLink) {
                    var halLink = (HalLink) halNode;
                    halLink.WriteAsXml(writer);
                } else if(halNode is HalProperty) {
                    var halProperty = (HalProperty)halNode;
                    halProperty.WriteAsXml(writer);
                } else if (halNode is HalResource) {
                    var halChildResource = (HalResource)halNode;
                    halChildResource.WriteAsXml(writer);
                }
            }
            writer.WriteEndElement();
        }

        public static void WriteAsXml(this HalLink halLink, XmlWriter writer) {
            writer.WriteStartElement("link");
            writer.WriteAttributeString("rel", halLink.Rel);
            writer.WriteAttributeString("href", halLink.Href);
            writer.WriteEndElement();

        }

        public static void WriteAsXml(this HalProperty halProperty, XmlWriter writer) {
            writer.WriteElementString(halProperty.Rel, halProperty.Value);

        }
        public static HalDocument ReadAsHal(this HttpContent content) {

            var element = XElement.Load(content.ContentReadStream);

            var doc = new HalDocument() {
                                            Root = element.ReadAsHalResource()
                                        };

            return doc;
        }


        public static HalResource ReadAsHalResource(this XElement element) {

            var resource = new HalResource() {
                                                 Name = element.ReadAttribute<string>("name"),
                                                 Href = element.ReadAttribute<string>("href"),
                                                 Rel = element.ReadAttribute<string>("rel")
                                             };

            resource.Contents = (from el in element.Elements()
                                 select HalNodeFactory(el)
                                 ).ToDictionary(re => re.Key);
            return resource;
        }

        public static HalNode HalNodeFactory(XElement element) {

            switch (element.Name.LocalName) {
                case "link":
                    return element.ReadAsHalLink();
                case "resource":
                    return element.ReadAsHalResource();
                default:
                    return new HalProperty() {
                                                 Rel = element.Name.LocalName,
                                                 Value = element.Value
                                             };
            }
        }

        public static HalLink ReadAsHalLink(this XElement element) {

            var link = new HalLink() {
                                         Href = element.ReadAttribute<string>("href"),
                                         Rel = element.ReadAttribute<string>("rel")
                                     };
            return link;
        }

        public static T ReadAttribute<T>(this XElement element, string name) {
            if (element.Attribute(name) == null) return default(T);
            return (T)Convert.ChangeType(element.Attribute(name).Value, typeof(T));
        }

        
    }
}